Imports Microsoft.VisualBasic
Imports System
Imports System.Collections.Generic
Imports System.Text
Imports System.Xml
Imports System.ComponentModel
Imports System.Globalization
Imports C4F.DevKit.WebServices.Datasets

Namespace C4F.DevKit.WebServices
	''' <summary>
	''' Exposes GetPost,AddPost and DeletePost functionality on del.icio.us
	''' </summary>
	<ToolboxItem(True)> _
	Public Class Delicious
		Inherits C4FWebServicesBase
		#Region "Private members"
		''' <summary>
		''' Specifies the user-name, if credentials are needed to access url
		''' </summary>
		Private urlUserName_Renamed As String = String.Empty
		''' <summary>
		''' Password for urlUserName, in case credentials are needed to access the url
		''' </summary>
		Private urlPassword_Renamed As String = String.Empty
		''' <summary>
		''' Base Url
		''' </summary>
        Private Shared baseUrl As String = My.Resources.DeliciousBaseUrl

		#End Region

		#Region "Accessors"
		''' <summary>
		''' Specify the user-name, if credentials are needed to access url
		''' </summary>
		<Category("Delicious"), Description("Specify the user-name, if credentials are needed to access url.")> _
		Public Property UrlUserName() As String
			Get
				Return Me.urlUserName_Renamed
			End Get
			Set(ByVal value As String)
				If value Is Nothing Then
					Me.urlUserName_Renamed = String.Empty
				Else
					Me.urlUserName_Renamed = value.Trim()
				End If
			End Set
		End Property

		''' <summary>
		''' Password for urlUserName, in case credentials are needed to access the url.
		''' This field is ignored if UserName is empty.
		''' </summary>
		<Category("Delicious"), Description("Password for urlUserName, in case credentials are needed to access the url. This field is ignored if UserName is empty.")> _
		Public Property UrlPassword() As String
			Get
				Return Me.urlPassword_Renamed
			End Get
			Set(ByVal value As String)
				If value Is Nothing Then
					Me.urlPassword_Renamed = String.Empty
				Else
					Me.urlPassword_Renamed = value.Trim()
				End If
			End Set
		End Property
		#End Region

		#Region "Public functions"

		''' <summary>
		''' Gets the bookmarks posted on Delicious by the user
		''' </summary>
		''' <returns>Returns all the posts of the user</returns>
		''' <exception cref="InvalidOperationException">The UrlUserName is not set (OR) the UrlPassword is not set</exception>
		Public Function GetPost() As List(Of DeliciousPost)
			If String.IsNullOrEmpty(Me.UrlUserName) OrElse String.IsNullOrEmpty(Me.UrlPassword) Then
                Throw New InvalidOperationException(String.Format(CultureInfo.InvariantCulture, My.Resources.ErrorSpecifyBothArguments, "UrlUserName", "UrlPassword"))
            End If

            Dim requestUrl As String = Delicious.GetRequestUrlForGetPost(Nothing, Nothing, Nothing)

            ' GetPost () responses
            Dim responseData As String = MyBase.GetQueryResponse(requestUrl, Me.UrlUserName, Me.UrlPassword)

            ' Parse the data and return results
            Return Delicious.ParseGetResponseXml(responseData)
        End Function

        ''' <summary>
        ''' Gets the bookmarks posted on Delicious by the user
        ''' </summary>
        ''' <param name="tag">Filter by this Category (optional)</param>
        ''' <param name="date">Filter by this Date (optional)</param>
        ''' <param name="url">Filter by this Url (optional)</param>
        ''' <returns>Returns the list of posts matching the arguments</returns>
        ''' <exception cref="InvalidOperationException">The UrlUserName is not set (OR) the UrlPassword is not set</exception>
        Public Function GetPost(ByVal tag As String, ByVal [date] As String, ByVal url As String) As List(Of DeliciousPost)
            If String.IsNullOrEmpty(Me.UrlUserName) OrElse String.IsNullOrEmpty(Me.UrlPassword) Then
                Throw New InvalidOperationException(String.Format(CultureInfo.InvariantCulture, My.Resources.ErrorSpecifyBothArguments, "UrlUserName", "UrlPassword"))
            End If

            Dim requestUrl As String = Delicious.GetRequestUrlForGetPost(tag, [date], url)

            ' GetPost () responses
            Dim responseData As String = MyBase.GetQueryResponse(requestUrl, Me.UrlUserName, Me.UrlPassword)

            ' Parse the data and return results
            Return Delicious.ParseGetResponseXml(responseData)
        End Function

        ''' <summary>
        ''' Add a post to del.icio.us
        ''' </summary>
        ''' <param name="url">The url of the item</param>
        ''' <param name="description">The description of the item</param>
        ''' <param name="extended">Notes for the item (optional)</param>
        ''' <param name="tags">Tags for the item (optional)</param>
        ''' <param name="dateStamp">Datestamp of the item (optional)</param>
        ''' <param name="replace">if 'no' dont replace post if given url has already been posted (optional)</param>
        ''' <param name="shared">if 'no' make the item private (optional)</param>
        ''' <returns>True if Add successful</returns>
        ''' <exception cref="InvalidOperationException">The UrlUserName is not set (OR) the UrlPassword is not set</exception>
        ''' <exception cref="ArgumentException">The url is not specified (OR) the description is not specified</exception>
        Public Function AddPost(ByVal url As String, ByVal description As String, ByVal extended As String, ByVal tags As String, ByVal dateStamp As String, ByVal replace As String, ByVal [shared] As String) As Boolean
            If String.IsNullOrEmpty(Me.UrlUserName) OrElse String.IsNullOrEmpty(Me.UrlPassword) Then
                Throw New InvalidOperationException(String.Format(CultureInfo.InvariantCulture, My.Resources.ErrorSpecifyBothArguments, "UrlUserName", "UrlPassword"))
            End If

            If String.IsNullOrEmpty(url) Then
                Throw New ArgumentException(String.Format(CultureInfo.InvariantCulture, My.Resources.ErrorSpecifyArgument, "url"))
            End If

            If String.IsNullOrEmpty(description) Then
                Throw New ArgumentException(String.Format(CultureInfo.InvariantCulture, My.Resources.ErrorSpecifyArgument, "description"))
            End If

            Dim requestUrl As String = Delicious.GetRequestUrlForAddPost(url, description, extended, tags, dateStamp, replace, [shared])

            ' GetPost () responses
            Dim responseData As String = MyBase.GetQueryResponse(requestUrl, Me.UrlUserName, Me.UrlPassword)

            ' Parse the data and return results
            Return Delicious.ParseAddDeleteResponseXml(responseData)

        End Function

        ''' <summary>
        ''' Add a post to del.icio.us
        ''' </summary>
        ''' <param name="url">The url of the item</param>
        ''' <param name="description">The description of the item</param>
        ''' <param name="extended">Notes for the item (optional)</param>
        ''' <param name="tags">Tags for the item (optional)</param>
        ''' <returns>True if Add successful</returns>
        ''' <exception cref="InvalidOperationException">The UrlUserName is not set (OR) the UrlPassword is not set</exception>
        ''' <exception cref="ArgumentException">The url is not specified (OR) the description is not specified</exception>
        Public Function AddPost(ByVal url As String, ByVal description As String, ByVal extended As String, ByVal tags As String) As Boolean
            If String.IsNullOrEmpty(Me.UrlUserName) OrElse String.IsNullOrEmpty(Me.UrlPassword) Then
                Throw New InvalidOperationException(String.Format(CultureInfo.InvariantCulture, My.Resources.ErrorSpecifyBothArguments, "UrlUserName", "UrlPassword"))
            End If

            If String.IsNullOrEmpty(url) Then
                Throw New ArgumentException(String.Format(CultureInfo.InvariantCulture, My.Resources.ErrorSpecifyArgument, "url"))
            End If

            If String.IsNullOrEmpty(description) Then
                Throw New ArgumentException(String.Format(CultureInfo.InvariantCulture, My.Resources.ErrorSpecifyArgument, "description"))
            End If

            Dim requestUrl As String = Delicious.GetRequestUrlForAddPost(url, description, extended, tags, Nothing, Nothing, Nothing)

            ' GetPost () responses
            Dim responseData As String = MyBase.GetQueryResponse(requestUrl, Me.UrlUserName, Me.UrlPassword)

            ' Parse the data and return results
            Return Delicious.ParseAddDeleteResponseXml(responseData)

        End Function

        ''' <summary>
        ''' Add a post to del.icio.us
        ''' </summary>
        ''' <param name="url">The url of the item</param>
        ''' <param name="description">The description of the item</param>
        ''' <returns>True if Add successful</returns>
        ''' <exception cref="InvalidOperationException">The UrlUserName is not set (OR) the UrlPassword is not set</exception>
        ''' <exception cref="ArgumentException">The url is not specified (OR) the description is not specified</exception>
        Public Function AddPost(ByVal url As String, ByVal description As String) As Boolean
            If String.IsNullOrEmpty(Me.UrlUserName) OrElse String.IsNullOrEmpty(Me.UrlPassword) Then
                Throw New InvalidOperationException(String.Format(CultureInfo.InvariantCulture, My.Resources.ErrorSpecifyBothArguments, "UrlUserName", "UrlPassword"))
            End If

            If String.IsNullOrEmpty(url) Then
                Throw New ArgumentException(String.Format(CultureInfo.InvariantCulture, My.Resources.ErrorSpecifyArgument, "url"))
            End If

            If String.IsNullOrEmpty(description) Then
                Throw New ArgumentException(String.Format(CultureInfo.InvariantCulture, My.Resources.ErrorSpecifyArgument, "description"))
            End If

            Dim requestUrl As String = Delicious.GetRequestUrlForAddPost(url, description, Nothing, Nothing, Nothing, Nothing, Nothing)

            ' GetPost () responses
            Dim responseData As String = MyBase.GetQueryResponse(requestUrl, Me.UrlUserName, Me.UrlPassword)

            ' Parse the data and return results
            Return Delicious.ParseAddDeleteResponseXml(responseData)
        End Function
        ''' <summary>
        ''' Delete a post from del.icio.us
        ''' </summary>
        ''' <param name="url">The url of the item</param>
        ''' <returns>successful if true</returns>
        ''' <exception cref="InvalidOperationException">The UrlUserName is not set (OR) the UrlPassword is not set</exception>
        ''' <exception cref="ArgumentNullException">The url is not specified</exception>
        Public Function DeletePost(ByVal url As String) As Boolean
            If String.IsNullOrEmpty(Me.UrlUserName) OrElse String.IsNullOrEmpty(Me.UrlPassword) Then
                Throw New InvalidOperationException(String.Format(CultureInfo.InvariantCulture, My.Resources.ErrorSpecifyBothArguments, "UrlUserName", "UrlPassword"))
            End If

            Dim requestUrl As String = Delicious.GetRequestUrlForDeletePost(url)

            ' GetPost () responses
            Dim responseData As String = MyBase.GetQueryResponse(requestUrl, Me.UrlUserName, Me.UrlPassword)

            ' Parse the data and return results
            Return Delicious.ParseAddDeleteResponseXml(responseData)
        End Function

		''' <summary>
		''' Gets the bookmarks posted on Delicious by the user and returns the result in a dataset
		''' </summary>
		''' <param name="tag">Filter by this Category (optional)</param>
		''' <param name="date">Filter by this Date (optional)</param>
		''' <param name="url">Filter by this Url (optional)</param>
		''' <returns>Dataset containing the list of posts matching the arguments</returns>
		''' <exception cref="InvalidOperationException">The UrlUserName is not set (OR) the UrlPassword is not set</exception>
		Public Function GetPost_DS(ByVal tag As String, ByVal [date] As String, ByVal url As String) As DS_Delicious

			Dim posts As List(Of DeliciousPost) = GetPost(tag, [date], url)
			Dim dataset As DS_Delicious = New DS_Delicious()
			For Each post As DeliciousPost In posts
				dataset.DeliciousPost.AddDeliciousPostRow(post.Url, post.Description, post.Extended, post.Hash, post.Others, post.Tag, post.Time)
			Next post
			Return dataset
		End Function

		''' <summary>
		''' Gets the bookmarks posted on Delicious by the user and returns the result in a dataset
		''' </summary>
		''' <returns>Dataset containing the list of all the posts of the user</returns>
		''' <exception cref="InvalidOperationException">The UrlUserName is not set (OR) the UrlPassword is not set</exception>
		Public Function GetPost_DS() As DS_Delicious
			Dim posts As List(Of DeliciousPost) = GetPost()
			Dim dataset As DS_Delicious = New DS_Delicious()
			For Each post As DeliciousPost In posts
				dataset.DeliciousPost.AddDeliciousPostRow(post.Url, post.Description, post.Extended, post.Hash, post.Others, post.Tag, post.Time)
			Next post
			Return dataset
		End Function
		#End Region

		#Region "Private functions"
		''' <summary>
		''' Prepare Request Url to get post items
		''' </summary>
		''' <param name="tag">Category of the post item</param>
		''' <param name="dt">Date</param>
		''' <param name="url">Url of the item</param>
		''' <returns>Request Url</returns>
		Private Shared Function GetRequestUrlForGetPost(ByVal tag As String, ByVal dt As String, ByVal url As String) As String
			Dim queryBuilder As StringBuilder = New StringBuilder(Delicious.baseUrl & "get?")

			' Add tag
			If (Not String.IsNullOrEmpty(tag)) AndAlso tag.Trim().Length > 0 Then
				queryBuilder.Append("&tag=")
				queryBuilder.Append(tag)
			End If

			' Add dt
			If (Not String.IsNullOrEmpty(dt)) AndAlso dt.Trim().Length > 0 Then
				queryBuilder.Append("&dt=")
				queryBuilder.Append(dt)
			End If

			' Add url
			If (Not String.IsNullOrEmpty(url)) AndAlso url.Trim().Length > 0 Then
				queryBuilder.Append("&url=")
				queryBuilder.Append(url)
			End If
			Return queryBuilder.ToString()
		End Function

		''' <summary>
		''' Parses Response data
		''' </summary>
		''' <param name="responseData">response Data</param>
		''' <returns>List of Post Items</returns>
		Private Shared Function ParseGetResponseXml(ByVal responseData As String) As List(Of DeliciousPost)
			Dim xmlDocument As XmlDocument = New XmlDocument()
			Dim postDataList As List(Of DeliciousPost) = Nothing

			xmlDocument.LoadXml(responseData)
			' Retrieve result nodes
			Dim postNodes As XmlNodeList = xmlDocument.DocumentElement.SelectNodes("/posts/post")

			If postNodes Is Nothing OrElse postNodes.Count = 0 Then
				Return postDataList
			End If

			postDataList = New List(Of DeliciousPost)(postNodes.Count)

			For i As Integer = 0 To postNodes.Count - 1
				Dim postNode As XmlNode = postNodes(i)
				Dim postData As DeliciousPost = New DeliciousPost()
				postDataList.Add(postData)

				' Populate PostData object
				If postNode Is Nothing OrElse postNode.Attributes Is Nothing OrElse postNode.Attributes.Count <= 0 Then
					Return postDataList
				End If

				If postNode.Attributes("href") Is Nothing Then
					postData.Url = Nothing
				Else
					postData.Url = postNode.Attributes("href").Value
				End If

				If postNode.Attributes("description") Is Nothing Then
					postData.Description = Nothing
				Else
					postData.Description = postNode.Attributes("description").Value
				End If

				If postNode.Attributes("extended") Is Nothing Then
					postData.Extended = Nothing
				Else
					postData.Extended = postNode.Attributes("extended").Value
				End If

				If postNode.Attributes("hash") Is Nothing Then
					postData.Hash = Nothing
				Else
					postData.Hash = postNode.Attributes("hash").Value
				End If

				If postNode.Attributes("others") Is Nothing Then
					postData.Others = Nothing
				Else
					postData.Others = postNode.Attributes("others").Value
				End If

				If postNode.Attributes("tag") Is Nothing Then
					postData.Tag = Nothing
				Else
					postData.Tag = postNode.Attributes("tag").Value
				End If

				If postNode.Attributes("time") Is Nothing Then
					postData.Time = Nothing
				Else
					postData.Time = postNode.Attributes("time").Value
				End If
			Next i
			Return postDataList
		End Function

		''' <summary>
		''' Prepare Request Url to add post items
		''' </summary>
		''' <param name="url">The url of the item</param>
		''' <param name="description">The description of the item</param>
		''' <param name="extended">Notes for the item</param>
		''' <param name="tags">Tags for the item tional)</param>
		''' <param name="dateStamp">Datestamp of the item</param>
		''' <param name="replace">if 'no' dont replace post if given url has already been posted (optional)</param>
		''' <param name="shared">if 'no' make the item private (optional)</param>
		''' <returns>True if Add successful</returns>
		Private Shared Function GetRequestUrlForAddPost(ByVal url As String, ByVal description As String, ByVal extended As String, ByVal tags As String, ByVal dateStamp As String, ByVal replace As String, ByVal [shared] As String) As String
			Dim queryBuilder As StringBuilder = New StringBuilder(Delicious.baseUrl & "add?")

			' Add url
			If (Not String.IsNullOrEmpty(url)) AndAlso url.Trim().Length > 0 Then
				queryBuilder.Append("&url=" & url)
			End If

			' Add description
			If (Not String.IsNullOrEmpty(description)) AndAlso description.Trim().Length > 0 Then
				queryBuilder.Append("&description=" & description)
			End If

			' Add extended
			If (Not String.IsNullOrEmpty(extended)) AndAlso extended.Trim().Length > 0 Then
				queryBuilder.Append("&extended=" & extended)
			End If

			' Add tags
			If (Not String.IsNullOrEmpty(tags)) AndAlso tags.Trim().Length > 0 Then
				queryBuilder.Append("&tags=" & tags)
			End If

			' Add dateStamp
			If (Not String.IsNullOrEmpty(dateStamp)) AndAlso dateStamp.Trim().Length > 0 Then
				queryBuilder.Append("&dt=" & dateStamp)
			End If

			' Add replace
			If (Not String.IsNullOrEmpty(replace)) AndAlso replace.Trim().Length > 0 Then
				queryBuilder.Append("&replace=" & replace)
			End If

			' Add shared
			If (Not String.IsNullOrEmpty([shared])) AndAlso [shared].Trim().Length > 0 Then
				queryBuilder.Append("&shared=" & [shared])
			End If

			Return queryBuilder.ToString()
		End Function

		''' <summary>
		''' Parses the response data to check whether add/delete successful 
		''' </summary>
		''' <param name="responseData">Response data</param>
		''' <returns>True is successful</returns>
		Private Shared Function ParseAddDeleteResponseXml(ByVal responseData As String) As Boolean
			Dim xmlDocument As XmlDocument = New XmlDocument()

			xmlDocument.LoadXml(responseData)

			' Retrieve result nodes
			Dim resultNode As XmlNode = xmlDocument.DocumentElement.SelectSingleNode("/result")

			If resultNode Is Nothing OrElse resultNode.Attributes Is Nothing OrElse resultNode.Attributes("code") Is Nothing Then
				Return False
			End If

			If String.Compare(resultNode.Attributes("code").Value, "done", False, CultureInfo.InvariantCulture) = 0 Then
				Return True
			End If

			Return False
		End Function

		''' <summary>
		''' Prepare request url to delete post item
		''' </summary>
		''' <param name="url">Url of the item</param>
		''' <returns>Request Url</returns>
		''' <exception cref="ArgumentNullException">The url is not specified</exception>
		Private Shared Function GetRequestUrlForDeletePost(ByVal url As String) As String
			Dim queryBuilder As StringBuilder = New StringBuilder(Delicious.baseUrl & "delete?")

			' Add url
			If (Not String.IsNullOrEmpty(url)) AndAlso url.Trim().Length > 0 Then
				queryBuilder.Append("&url=" & url)
			Else
				Throw New ArgumentNullException("url")
			End If

			Return queryBuilder.ToString()
		End Function
		#End Region
	End Class

	''' <summary>
	''' Represents a post on del.icio.us
	''' </summary>
	Public Class DeliciousPost
		#Region "Private Members"
		''' <summary>
		''' Book mark URL
		''' </summary>
		Private url_Renamed As String
		''' <summary>
		''' Description of the book mark
		''' </summary>
		Private description_Renamed As String
		''' <summary>
		''' Extended Note about the book mark
		''' </summary>
		Private extended_Renamed As String
		''' <summary>
		''' Hash value
		''' </summary>
		Private hash_Renamed As String
		''' <summary>
		''' No. of other people that have book marked the same URL
		''' </summary>
		Private others_Renamed As String
		''' <summary>
		''' Category
		''' </summary>
		Private tag_Renamed As String
		''' <summary>
		''' Time when posted
		''' </summary>
		Private time_Renamed As String
		#End Region

		#Region "Public Properties"
		''' <summary>
		''' Book mark URL
		''' </summary>
		Public Property Url() As String
			Get
				Return Me.url_Renamed
			End Get
			Set(ByVal value As String)
				Me.url_Renamed = value
			End Set
		End Property
		''' <summary>
		''' Description of the book mark
		''' </summary>
		Public Property Description() As String
			Get
				Return Me.description_Renamed
			End Get
			Set(ByVal value As String)
				Me.description_Renamed = value
			End Set
		End Property
		''' <summary>
		''' Extended Note about the book mark
		''' </summary>
		Public Property Extended() As String
			Get
				Return Me.extended_Renamed
			End Get
			Set(ByVal value As String)
				Me.extended_Renamed = value
			End Set
		End Property
		''' <summary>
		''' Hash value
		''' </summary>
		Public Property Hash() As String
			Get
				Return Me.hash_Renamed
			End Get
			Set(ByVal value As String)
				Me.hash_Renamed = value
			End Set
		End Property
		''' <summary>
		''' No. of other people that have book marked the same URL
		''' </summary>
		Public Property Others() As String
			Get
				Return Me.others_Renamed
			End Get
			Set(ByVal value As String)
				Me.others_Renamed = value
			End Set
		End Property
		''' <summary>
		''' Category
		''' </summary>
		Public Property Tag() As String
			Get
				Return Me.tag_Renamed
			End Get
			Set(ByVal value As String)
				Me.tag_Renamed = value
			End Set
		End Property
		''' <summary>
		''' Time when posted
		''' </summary>
		Public Property Time() As String
			Get
				Return Me.time_Renamed
			End Get
			Set(ByVal value As String)
				Me.time_Renamed = value
			End Set
		End Property
		#End Region
	End Class
End Namespace